MB2_MAGIC       EQU 0xE85250D6
MB2_ARCH        EQU 0
MB2_HDRLEN      EQU (Mb2HdrEnd - Mb2Hdr)
MB2_CHECKSUM    EQU -(MB2_MAGIC + MB2_ARCH + MB2_HDRLEN)

[SECTION .header]
[EXTERN _rstart]
[EXTERN _endofExec]
[EXTERN _ebss]
ALIGN 64
Mb2Hdr:
    DD  MB2_MAGIC
    DD  MB2_ARCH
    DD  MB2_HDRLEN
    DD  MB2_CHECKSUM   

    DW  2
    DW  1
    DD  24
    DD  Mb2Hdr
    DD  _rstart
    DD  _endofExec - 0xFFFFFFFF80000000
    DD  _ebss - 0xFFFFFFFF80000000

    DW  3
    DW  1
    DD  12
    DD  _entry

    DW  0, 0
    DD  8
Mb2HdrEnd:

[BITS 32]
[SECTION .text]
[GLOBAL _entry]
[EXTERN PreInit]
[EXTERN KernelPml4]
_entry:
    mov edx, eax
    mov eax, gdtr
    lgdt [eax]

    jmp 0x18:.gdtDone

.gdtDone:
    mov eax, 0x20
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax

    mov esp, Stack + 0x8000

    ; Save multiboot informations
    push ebx
    push edx

    ; Enable PAE and SSE instructions support
    mov eax, cr4
    or eax, 0x620
    mov cr4, eax

    call SetupPaging

    mov eax, KernelPml4
    mov cr3, eax

    ; Enable long mode
    mov ecx, 0xC0000080
    rdmsr
    or eax, 1 << 8
    wrmsr

    ; Enable Paging and MP and clear EM to enable full SSE support
    mov eax, cr0
    or eax, 0x80000002
    and eax, 0xFFFFFFFB
    mov cr0, eax

    mov eax, gdtr64
    lgdt [eax]
    pop edx
    pop ebx
    jmp 0x08:LongMode

[EXTERN KernelPml4]
[EXTERN KernelPdpt]
[EXTERN KernelPd1]
[EXTERN KernelPd2]
SetupPaging:
    mov eax, KernelPdpt
    or eax, 1

    mov edi, KernelPml4
    mov [edi], eax
    mov [edi + 4088], eax

    mov eax, KernelPd1
    or eax, 1

    mov edi, KernelPdpt
    mov [edi], eax
    mov [edi + 4080], eax

    mov edi, KernelPd1
    mov [edi], DWORD 0x83
    mov [edi + 8], DWORD 0x20083
    mov [edi + 16], DWORD 0x40083
    mov [edi + 24], DWORD 0x60083
    mov [edi + 32], DWORD 0x80083
    mov [edi + 40], DWORD 0xA0083
    mov [edi + 48], DWORD 0xC0083
    mov [edi + 56], DWORD 0xE0083

    ret

[SECTION .data]

gdt:
    DD  0, 0
    
    ; 64-bit kernel code & data
code64:
	dw 0
	dw 0
	db 0
	db 0x9A
	db 0xA0
	db 0

data64:
	dw 0
	dw 0
	db 0
	db 0x92
	db 0xA0
	db 0

    ; 32-bit loader segments. Will be removed later by the kernel
code:
	dw 0xFFFF
	dw 0x0000
	db 0x00
	db 0x9A
	db 0xCF
	db 0x00

data:
	dw 0xFFFF
	dw 0x0000
	db 0x00
	db 0x92
	db 0xCF
	db 0x00

gdtr:
    DW  0x27
    DD  gdt

gdtr64:
    DW  0x17
    DD  gdt

[SECTION .bss]
Stack:
    RESB 0x8000

[BITS 64]
[SECTION .text]
[EXTERN _ZN6Kernel4InitEmm]
LongMode:
	mov ax, 0x10
	mov ds, ax
	mov es, ax
	mov ss, ax

	mov rsp, Stack + 0x8000 + 0xFFFFFFFF80000000

	; Initialize the fpu
	finit

	; Pass grub multiboot header as argument
	mov rdi, 0
	mov edi, ebx

        mov rsi, 0
        mov esi, edx

	; Let's go!
	mov rax, _ZN6Kernel4InitEmm
	call rax

hltLoop:
	hlt
	jmp hltLoop